package tcp

import (
	"encoding/binary"
	"fmt"
	tErr "gse/transport/errors"
	"io"
	"net"
	"strings"
	"sync"
)

type Connection struct {
	wlock *sync.Mutex
	rlock *sync.Mutex
	*net.TCPConn
}

type TCPConnectionConfig struct {
	NoDelay bool
}

func NewTcpConnection(conn *net.TCPConn, cfg *TCPConnectionConfig) *Connection {

	c := &Connection{
		wlock:   new(sync.Mutex),
		rlock:   new(sync.Mutex),
		TCPConn: conn,
	}
	conn.SetNoDelay(cfg.NoDelay)
	return c
}

func isNetBrokenErr(err error) bool {
	return err.Error() == "EOF" || strings.Contains(err.Error(), "broken") || strings.Contains(err.Error(), "closed")
}

func (c *Connection) SendData(data []byte) (int, error) {
	c.wlock.Lock()
	c.wlock.Unlock()
	n, err := c.Write(data)
	if err != nil {
		if isNetBrokenErr(err) {
			return n, tErr.ConectionClosed
		}
	}
	return n, err
}

// ReadData read a complete request/transaction data in block mode.
func (c *Connection) ReadData() ([]byte, error) {
	c.rlock.Lock()
	defer c.rlock.Unlock()
	protoVer := make([]byte, 1)
	_, err := io.ReadFull(c, protoVer)
	if err != nil {
		if isNetBrokenErr(err) {
			return nil, tErr.ConectionClosed
		}
		return nil, fmt.Errorf("failed to read protocol version : %v", err)
	}
	lenB := make([]byte, 4)
	_, err = io.ReadFull(c, lenB)
	if err != nil {
		if isNetBrokenErr(err) {
			return nil, tErr.ConectionClosed
		}
		return nil, fmt.Errorf("failed to read data length : %v", err)
	}
	packetLen := binary.BigEndian.Uint32(lenB)
	packetData := make([]byte, int(packetLen)+1+4)
	packetData[0] = protoVer[0]
	copy(packetData[1:5], lenB)
	_, err = io.ReadFull(c, packetData[5:])
	if err != nil {
		if isNetBrokenErr(err) {
			return nil, tErr.ConectionClosed
		}
		return nil, fmt.Errorf("failed to read packet data : %v", err)
	}
	return packetData, nil
}

func (c *Connection) RemoteAddr() net.Addr {
	return c.TCPConn.RemoteAddr()
}

func (c *Connection) LocalAddr() net.Addr {
	return c.TCPConn.LocalAddr()
}

func (c *Connection) Close() {
	c.TCPConn.Close()
}
